home *** CD-ROM | disk | FTP | other *** search
- /* Animated Cursor subroutines.
-
- Copyright © 1989 by Michael S. Morton, all rights reserved.
- Written February ’89 for MacTutor. Feel free to use these or
- modify them so long as the original author and source are noted.
-
- Routines included are:
- • acStartHG -- set the cursor to be an hourglass
- • acStartW1, acStartW2 -- set the cursor to be a 1- or 2-handed watch
- • acDelay -- set the delay, in ticks, between animation steps
- • acNext -- idle-time routine
-
- To set up any of these cursors, call the acStart___ routine, passing the
- delay you’d like between frames. Then call acNext during your idle
- routine. To change the delay, call acDelay. To get a different kind
- of cursor, stop calling acNext and use SetCursor to get it.
- */
-
- /* State information for three cursor types: */
- typedef struct /* info on the HOURGLASS: */
- { long coin; /* used in random number generator */
- int grainRow; /* row number (0…15) of falling grain */
- int grainBit; /* bit mask for falling grain */
- Boolean grainMoving; /* flag: is the grain still moving? */
- } hourInfo; /* define this type */
-
- typedef struct /* info on ONE-HANDED WATCH: */
- { int wFrame; /* which frame of animation are we on? */
- } watch1Info; /* define this type */
-
- typedef struct /* info on TWO-HANDED WATCH: */
- { int mFrame; /* frame of minute hand */
- int hFrame; /* frame of hour hand */
- } watch2Info; /* define this type */
-
- /* Generalized cursor type: */
- typedef enum
- { hgType, w1Type, w2Type } cursType; /* selects one of above structs */
-
- typedef struct /* tracks any kind of cursor */
- { Cursor curs; /* current cursor */
- int delay; /* delay between frames (ticks) */
- long nextFrame; /* time of next frame (ticks) */
- cursType type; /* type of cursor */
- union /* depending on type of cursor: */
- { hourInfo hg; /* • info on hourglass */
- watch1Info w1; /* • info on one-handed watch */
- watch2Info w2; /* • info on two-handed watch */
- } u; /* give union an easy name */
- } cursInfo, *ciPtr;
-
- /* The cursor information is used globally all through this file. */
- cursInfo theInfo; /* sole instance of this struct */
-
- /* 16x16 data and masks for cursors. Some of these are OR’d together to
- make the two-handed watch. Others are just backgrounds which are
- drawn on, such as for the hourglass. */
- int hourData [16] = /* hourglass, about to start falling */
- { 0xFFFE, 0x7FFC, 0x7FFC, 0x7FFC, 0x5FF4, 0x4FE4, 0x47C4, 0x4384,
- 0x4284, 0x46C4, 0x4C64, 0x5834, 0x701C, 0x600C, 0x4004, 0xFFFE };
- int hourMask [16] =
- { 0xFFFE, 0x7FFC, 0x7FFC, 0x7FFC, 0x5FF4, 0x4FE4, 0x47C4, 0x4384,
- 0x4384, 0x47C4, 0x4FE4, 0x5FF4, 0x7FFC, 0x7FFC, 0x7FFC, 0xFFFE };
-
- int watchMask [16] =
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x7F00, 0xFF80, 0xFF80, 0xFFC0,
- 0xFFC0, 0xFF80, 0x7F00, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 };
-
- int watchMinutes [12][16] = /* twelve minute-hand positions */
- {
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4900, 0x8880, 0x8880, 0x88C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4300, 0x8480, 0x8480, 0x88C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8180, 0x8680, 0x88C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8FC0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
- 0x86C0, 0x8180, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
- 0x84C0, 0x8480, 0x4300, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
- 0x88C0, 0x8880, 0x4900, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
- 0x90C0, 0x9080, 0x6100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x88C0,
- 0xB0C0, 0xC080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0xF8C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0xC080, 0xB080, 0x88C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x6100, 0x9080, 0x9080, 0x88C0,
- 0x80C0, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 }
- };
-
- int watchHours [8][16] = /* eight hour-hand positions */
- {
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8880, 0x8880, 0x8880,
- 0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8480, 0x8880,
- 0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8E80,
- 0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
- 0x8480, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
- 0x8880, 0x8880, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0x8880,
- 0x9080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x8080, 0xB880,
- 0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 },
- { 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x4100, 0x8080, 0x9080, 0x8880,
- 0x8080, 0x8080, 0x4100, 0x3E00, 0x3E00, 0x3E00, 0x3E00, 0x0000 }
- };
-
- /* Function prototypes for stuff we define externally: */
- #include "AnimCurs.h"
-
- /* Function prototypes for our static stuff: */
- void acStartW (cursType type, int delay); /* start either kind of watch */
- void copy16 (int *src, Bits16 *dst); /* copy a 16x16 bit image */
- void or16 (int *src, Bits16 *dst); /* OR a 16x16 bit image */
- void hideGrain (void); /* hide current grain in hourglass */
- void showGrain (void); /* show current grain in hourglass */
- Boolean coinFlip (void); /* produce a pseudo-random boolean */
- void stealGrain (void); /* remove a grain from top section */
- void dropGrain (void); /* get a grain moving */
- void moveGrain (int dh, int dv); /* move a grain in transit */
- void nextHour (void); /* compute next cursor: hourglass */
- void nextWatch (void); /* watch */
- void nextWatch2 (void); /* 2-handed watch */
- void doNextFrame (void); /* compute next frame and show it */
-
-
- /* acStartHG () -- Initialize the hourglass cursor, with the specified delay
- between animation frames. This is an external routine. */
- void acStartHG (delay)
- int delay; /* INPUT: pause between frames */
- { theInfo.delay = delay; /* record delay in structure */
- theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
- theInfo.type = hgType; /* remember the type: hourglass */
- theInfo.u.hg.grainRow = -1; /* make a grain drop in dropGrain () */
-
- /* Initialize cursor data, mask and hotspot (altho dropGrain does data, too) */
- copy16 (hourData, & (theInfo.curs.data));
- copy16 (hourMask, & (theInfo.curs.mask));
- theInfo.curs.hotSpot.v = 8; /* stick hotspot… */
- theInfo.curs.hotSpot.h = 8; /* …in middle */
-
- dropGrain (); /* start first grain dropping */
- } /* end of acStartHG () */
-
- /* acStartW1 () -- Just like acStartHG, but with a one-handed watch.
- This is an external routine.
- */
- void acStartW1 (delay)
- int delay; /* INPUT: pause between frames */
- { acStartW (w1Type, delay);
- } /* end of acStartW1 () */
-
- /* acStartW2 () -- Just like acStartHG, but with a two-handed watch.
- This is an external routine.
- */
- void acStartW2 (delay)
- int delay; /* INPUT: pause between frames */
- { acStartW (w2Type, delay);
- } /* end of acStartW2 () */
-
- /* acStartW () -- Start a one- or two-handed watch. */
- static void acStartW (type, delay)
- cursType type; /* INPUT: w1Type or w2Type */
- int delay; /* INPUT: pause between frames */
- { theInfo.delay = delay; /* record delay in structure */
- theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
- theInfo.type = type; /* remember the type */
-
- if (type == w1Type) /* init different types: */
- theInfo.u.w1.wFrame = 0; /* one-handed: set frame to zero */
- else
- { theInfo.u.w2.mFrame = 0; /* two-handed: set minute frame… */
- theInfo.u.w2.hFrame = 0; } /* …and hour frame to zero */
-
- /* Initialize the cursor data, mask and hotspot: */
- copy16 (watchMinutes [0], & (theInfo.curs.data));
- copy16 (watchMask, & (theInfo.curs.mask));
- theInfo.curs.hotSpot.v = 8; /* stick hotspot… */
- theInfo.curs.hotSpot.h = 8; /* …in middle */
- } /* end of acStartW () */
-
- /* copy16 () -- Copy an array of 16 ints into a Bits16 structure (the data
- or mask of a cursor. */
- void copy16 (src, bits)
- register int *src; /* INPUT: source data */
- Bits16 *bits; /* OUTPUT: where to stuff it */
- { register int *dst = (int *) bits; /* coerce to a handier data type */
- int i; /* FORTRAN-ish loop counter */
-
- for (i = 0; i <= 15; i++) *dst++ = *src++; /* copy 16 words */
- } /* end of copy16 () */
-
- /* or16 () -- Just like copy16(), but we OR, not COPY. */
- void or16 (src, bits)
- register int *src; /* INPUT: source data */
- Bits16 *bits; /* OUTPUT: where to stuff it */
- { register int *dst = (int *) bits; /* coerce to a handier data type */
- int i; /* FORTRAN-ish loop counter */
-
- for (i = 0; i <= 15; i++) *dst++ |= *src++; /* OR 16 words */
- } /* end of or16 () */
-
- /* hideGrain () -- Hide the pixel where the current grain is. */
- static void hideGrain ()
- { theInfo.curs.data [theInfo.u.hg.grainRow] &=
- (~ theInfo.u.hg.grainBit);
- }
-
- /* showGrain () -- Show the pixel where the current grain is. */
- static void showGrain ()
- { theInfo.curs.data [theInfo.u.hg.grainRow] |=
- theInfo.u.hg.grainBit;
- }
-
- /* coinFlip () -- Return a pseudo-random boolean value. We use a shift-
- register system detailed in Knuth, among other places. */
- static Boolean coinFlip ()
- { register Boolean result;
-
- if (! theInfo.u.hg.coin) /* zero? */
- theInfo.u.hg.coin = 1; /* avoid demon state */
- result = (theInfo.u.hg.coin < 0); /* note if high bit is set */
-
- theInfo.u.hg.coin <<= 1; /* shift old value over 1 bit… */
- if (result) /* …and if top bit was ‘1’… */
- theInfo.u.hg.coin ^= 0xc5; /* …then futz with low bits */
-
- return (result);
- } /* end of coinFlip () */
-
- /* stealGrain -- Grab a grain of sand from the top chamber of the hourglass.
- We want to percolate a white pixel up from the starting bit position. */
- static void stealGrain ()
- { register int v = 6; /* start at row 6, tip of top half */
- register int hBit = 0x0100; /* start at the middle */
- register Boolean done = false; /* haven’t finished percolating yet */
- register int aboveWord; /* row of sand grains above us */
- register Boolean leftOK, rightOK; /* are there bits to steal above? */
-
- while (! done)
- { --v; /* assume we can move up a row */
- aboveWord = theInfo.curs.data [v]; /* get that row from the cursor image */
-
- /* Decide which direction to steal from -- if there's a grain right above
- us, we’ll take that. Otherwise, we have to choose between the ones
- diagonally up from us. */
- if ((aboveWord & hBit) == 0) /* have a bit above us? */
- { leftOK = (0 != (aboveWord & (hBit << 1))); /* have someone above on left? */
- rightOK = (0 != (aboveWord & (hBit >> 1))); /* someone above on right? */
-
- /* Branch, depending on which way(s) we can go: */
- if (leftOK && rightOK) /* both available? */
- { if (coinFlip ()) hBit <<= 1; /* yes: flip a coin… */
- else hBit >>= 1; /* …to decide */
- }
- else if (leftOK) hBit <<= 1; /* just left: take it */
- else if (rightOK) hBit >>= 1; /* just right: take it */
- else { ++v; done = true; } /* neither: back down */
- } /* end of no bit above us */
-
- if (v <= 1) done = true; /* if we hit top row, we have to stop */
- } /* end of while-not-done */
-
- theInfo.curs.data [v] &= (~ hBit); /* snuff this bit */
- } /* end of stealGrain () */
-
- /* dropGrain () -- Get next grain of sand to fall. The “moving” flag is false.
-
- We get one grain of sand from the top chamber. If the bottom chamber is
- filled, we reinit the cursor to a full top chamber. Either way, we flag
- that a grain is moving, and draw it.
- */
- static void dropGrain ()
- { stealGrain (); /* pull a grain from the top chamber */
-
- /* If the pile in the bottom chamber has filled it, the most recent
- grain dropped didn’t get very far. */
- if (theInfo.u.hg.grainRow < 9) /* are we piled too high? */
- copy16 (hourData, & (theInfo.curs.data)); /* yes: redraw all */
-
- theInfo.u.hg.grainRow = 8; /* set row number of new grain */
- theInfo.u.hg.grainBit = 0x0100; /* set bit in row */
- theInfo.u.hg.grainMoving = true; /* we’re cooking with gas now */
- showGrain (); /* show the grain */
- } /* end of dropGrain () */
-
- /* moveGrain () -- Move the current grain by a given delta. Note that a
- positive “dh” is a right shift. */
- static void moveGrain (dh, dv)
- int dh, dv; /* INPUT: amount to move by */
- { hideGrain (); /* hide where it is now */
-
- theInfo.u.hg.grainRow += dv; /* update vertical position */
- if (dh > 0) /* update horizontal position */
- theInfo.u.hg.grainBit >>= dh;
- else theInfo.u.hg.grainBit <<= -dh;
-
- showGrain (); /* show where it is now */
- } /* end of moveGrain () */
-
- /* nextHour () -- Advance the hourglass by one frame. */
- static void nextHour ()
- { register int v, hBit; /* coordinate and bit pos of grain */
- register int nextWord; /* word of grains below us */
- register Boolean leftOK, rightOK; /* flags for OK directions to fall */
-
- if (! theInfo.u.hg.grainMoving) /* grain hit bottom last time? */
- dropGrain (); /* yes: get a new one */
-
- v = theInfo.u.hg.grainRow; /* grab position and bit just… */
- hBit = theInfo.u.hg.grainBit; /* …for easier typing & efficiency */
-
- nextWord = theInfo.curs.data [v+1]; /* get word below this one */
- if ((nextWord & hBit) == 0) /* slot below us free? */
- { moveGrain (0, 1); /* yes: just move down one */
- return; /* and that’s all we need */
- }
-
- leftOK = (0 == (nextWord & (hBit << 1))); /* have nobody below on left? */
- rightOK = (0 == (nextWord & (hBit >> 1))); /* nobody below on right? */
-
- /* Branch, depending on which way(s) we can go: */
- if (leftOK && rightOK) /* can fall left or right? */
- { if (coinFlip ()) /* yes: randomly choose: */
- moveGrain (1, 1); /* • fall to the right */
- else moveGrain (-1, 1); /* • fall to the left */
- }
- else if (leftOK) moveGrain (-1, 1); /* left only: do it */
- else if (rightOK) moveGrain (1, 1); /* right only: do it */
- else theInfo.u.hg.grainMoving = false; /* can’t move: stop; reset next time */
- } /* end of nextHour () */
-
- /* nextWatch -- Compute the next frame of one-handed watch animation. */
- static void nextWatch ()
- { ++ theInfo.u.w1.wFrame; /* bump frame ahead by 1 */
- if ((theInfo.u.w1.wFrame) >= 12) /* outside of range 0…11? */
- theInfo.u.w1.wFrame = 0; /* yes: wrap around */
-
- copy16 (watchMinutes [theInfo.u.w1.wFrame],
- & (theInfo.curs.data));
- } /* end of nextWatch () */
-
- /* nextWatch2 -- Compute the next frame of two-handed watch animation. */
- static void nextWatch2 ()
- { ++ theInfo.u.w2.mFrame; /* bump MINUTE frame ahead by 1 */
- if ((theInfo.u.w2.mFrame) >= 12) /* outside of range 0…11? */
- theInfo.u.w2.mFrame = 0; /* yes: wrap around */
-
- copy16 (watchMinutes [theInfo.u.w2.mFrame],
- & (theInfo.curs.data));
-
- if (theInfo.u.w2.mFrame == 0) /* MINUTE frame just hit 12 o’clock? */
- { ++ theInfo.u.w2.hFrame; /* yes: bump HOUR frame ahead by 1 */
- if ((theInfo.u.w2.hFrame) >= 8) /* outside of range 0…7? */
- theInfo.u.w2.hFrame = 0; /* yes: wrap around */
- }
-
- /* Add hour-hand image into minute-hand. */
- or16 (watchHours [theInfo.u.w2.hFrame],
- & (theInfo.curs.data));
- } /* end of nextWatch () */
-
- /* doNextFrame -- Compute next frame, dispatching on type of cursor. */
- static void doNextFrame ()
- { switch (theInfo.type)
- { case hgType: nextHour (); break;
- case w1Type: nextWatch (); break;
- case w2Type: nextWatch2 (); break;
- default: break;
- } /* end of switch on cursor type */
- } /* end of doNextFrame () */
-
- /* acNext -- Idle-time animation. This is an external routine.
- */
- void acNext ()
- { if (TickCount () < theInfo.nextFrame) /* time to animate yet? */
- return; /* nope: return */
-
- theInfo.nextFrame = TickCount () + theInfo.delay; /* reset wake-up call */
- doNextFrame (); /* draw next frame internally */
- SetCursor (& (theInfo.curs)); /* and display it */
- } /* end of acNext () */
-
- /* acDelay -- Set delay, in ticks, between frames. This is an external routine.
- */
- void acDelay (delay)
- int delay; /* INPUT: new delay */
- { theInfo.delay = delay;
- theInfo.nextFrame = TickCount () + delay; /* figure when to animate next */
- } /* end of acDelay () */